"""
模型评估
"""
import numpy as np
from sklearn.metrics import (mean_absolute_error,
                             mean_squared_error)
from statsmodels.tsa.api import stattools
import matplotlib.pyplot as plt
from pylab import mpl


# 指定默认字体
mpl.rcParams['font.sans-serif'] = ['SimHei']
# 解决保存图像是负号'-'显示为方块的问题
mpl.rcParams['axes.unicode_minus'] = False


class Evalute:
    """
    模型评估
    """
    def __init__(self, y_true, y_pred, low=None, high=None, k=1):
        self.y_true = y_true
        self.y_pred = y_pred
        self.low = low
        self.high = high
        self.k = k

    def evalute_error_index(self):
        """
        评估指标
        :return:
        """
        # 误差/残差评估
        mae = mean_absolute_error(y_true=self.y_true, y_pred=self.y_pred)
        mse = mean_squared_error(y_true=self.y_true, y_pred=self.y_pred)

        # 信息准则评估
        aic = len(self.y_true) * np.log(mse) + 2 * (self.k + 2)
        aicc = \
            aic + (2 * (self.k + 2) * (self.k + 3)) / (len(self.y_true) -
                                                       self.k - 3)
        bic = \
            len(self.y_true) * np.log(mse) + \
            (self.k + 2) * np.log(len(self.y_true))

        return mae, mse, aic, aicc, bic

    def accuracy(self):
        """
        准确率
        :return:
        """
        if self.low and self.high:
            corr_num = 0
            for idx, num in enumerate(self.y_true):
                if self.low[idx] <= num <= self.high[idx]:
                    corr_num += 1
            return corr_num / len(self.y_true)


def check_noise(err):
    """
    判断序列是否为白噪音
    :param err:
    :return:
    """
    #  误差白噪音检验
    #  对于长度为T的白噪音序列，我们希望在95%的置信度下，
    #  它的自相关值(acf)属于区间[-2/sqrt(T), 2/sqrt(T)]
    acf = stattools.acf(x=err)
    min_acf = -2 / np.sqrt(len(err))
    max_acf = 2 / np.sqrt(len(err))
    acf_value = [1 if num < min_acf or num > max_acf else 0 for num in acf]

    if sum(acf_value) / len(err) > 0.05:
        print('acf区间置信度检测结果：误差不是白噪音')
    else:
        print('acf区间置信度检测结果：误差是白噪音')

    #  误差分布
    err.plot()
    plt.title('误差')
    plt.show()
